﻿using System;
using UniRx.InternalUtil;

namespace UniRx
{
	// Token: 0x02000393 RID: 915
	public sealed class Subject<T> : ISubject<T>, IDisposable, IOptimizedObservable<T>, ISubject<T, T>, IObserver<T>, IObservable<T>
	{
		// Token: 0x17000123 RID: 291
		// (get) Token: 0x06001297 RID: 4759 RVA: 0x00055B5A File Offset: 0x00053F5A
		public bool HasObservers
		{
			get
			{
				return !(this.outObserver is EmptyObserver<T>) && !this.isStopped && !this.isDisposed;
			}
		}

		// Token: 0x06001298 RID: 4760 RVA: 0x00055B84 File Offset: 0x00053F84
		public void OnCompleted()
		{
			object obj = this.observerLock;
			IObserver<T> observer;
			lock (obj)
			{
				this.ThrowIfDisposed();
				if (this.isStopped)
				{
					return;
				}
				observer = this.outObserver;
				this.outObserver = EmptyObserver<T>.Instance;
				this.isStopped = true;
			}
			observer.OnCompleted();
		}

		// Token: 0x06001299 RID: 4761 RVA: 0x00055BF0 File Offset: 0x00053FF0
		public void OnError(Exception error)
		{
			if (error == null)
			{
				throw new ArgumentNullException("error");
			}
			object obj = this.observerLock;
			IObserver<T> observer;
			lock (obj)
			{
				this.ThrowIfDisposed();
				if (this.isStopped)
				{
					return;
				}
				observer = this.outObserver;
				this.outObserver = EmptyObserver<T>.Instance;
				this.isStopped = true;
				this.lastError = error;
			}
			observer.OnError(error);
		}

		// Token: 0x0600129A RID: 4762 RVA: 0x00055C74 File Offset: 0x00054074
		public void OnNext(T value)
		{
			this.outObserver.OnNext(value);
		}

		// Token: 0x0600129B RID: 4763 RVA: 0x00055C84 File Offset: 0x00054084
		public IDisposable Subscribe(IObserver<T> observer)
		{
			if (observer == null)
			{
				throw new ArgumentNullException("observer");
			}
			Exception ex = null;
			object obj = this.observerLock;
			lock (obj)
			{
				this.ThrowIfDisposed();
				if (!this.isStopped)
				{
					ListObserver<T> listObserver = this.outObserver as ListObserver<T>;
					if (listObserver != null)
					{
						this.outObserver = listObserver.Add(observer);
					}
					else
					{
						IObserver<T> observer2 = this.outObserver;
						if (observer2 is EmptyObserver<T>)
						{
							this.outObserver = observer;
						}
						else
						{
							this.outObserver = new ListObserver<T>(new ImmutableList<IObserver<T>>(new IObserver<T>[]
							{
								observer2,
								observer
							}));
						}
					}
					return new Subject<T>.Subscription(this, observer);
				}
				ex = this.lastError;
			}
			if (ex != null)
			{
				observer.OnError(ex);
			}
			else
			{
				observer.OnCompleted();
			}
			return Disposable.Empty;
		}

		// Token: 0x0600129C RID: 4764 RVA: 0x00055D74 File Offset: 0x00054174
		public void Dispose()
		{
			object obj = this.observerLock;
			lock (obj)
			{
				this.isDisposed = true;
				this.outObserver = DisposedObserver<T>.Instance;
			}
		}

		// Token: 0x0600129D RID: 4765 RVA: 0x00055DBC File Offset: 0x000541BC
		private void ThrowIfDisposed()
		{
			if (this.isDisposed)
			{
				throw new ObjectDisposedException(string.Empty);
			}
		}

		// Token: 0x0600129E RID: 4766 RVA: 0x00055DD4 File Offset: 0x000541D4
		public bool IsRequiredSubscribeOnCurrentThread()
		{
			return false;
		}

		// Token: 0x04000B35 RID: 2869
		private object observerLock = new object();

		// Token: 0x04000B36 RID: 2870
		private bool isStopped;

		// Token: 0x04000B37 RID: 2871
		private bool isDisposed;

		// Token: 0x04000B38 RID: 2872
		private Exception lastError;

		// Token: 0x04000B39 RID: 2873
		private IObserver<T> outObserver = EmptyObserver<T>.Instance;

		// Token: 0x02000394 RID: 916
		private class Subscription : IDisposable
		{
			// Token: 0x0600129F RID: 4767 RVA: 0x00055DD7 File Offset: 0x000541D7
			public Subscription(Subject<T> parent, IObserver<T> unsubscribeTarget)
			{
				this.parent = parent;
				this.unsubscribeTarget = unsubscribeTarget;
			}

			// Token: 0x060012A0 RID: 4768 RVA: 0x00055DF8 File Offset: 0x000541F8
			public void Dispose()
			{
				object obj = this.gate;
				lock (obj)
				{
					if (this.parent != null)
					{
						object observerLock = this.parent.observerLock;
						lock (observerLock)
						{
							ListObserver<T> listObserver = this.parent.outObserver as ListObserver<T>;
							if (listObserver != null)
							{
								this.parent.outObserver = listObserver.Remove(this.unsubscribeTarget);
							}
							else
							{
								this.parent.outObserver = EmptyObserver<T>.Instance;
							}
							this.unsubscribeTarget = null;
							this.parent = null;
						}
					}
				}
			}

			// Token: 0x04000B3A RID: 2874
			private readonly object gate = new object();

			// Token: 0x04000B3B RID: 2875
			private Subject<T> parent;

			// Token: 0x04000B3C RID: 2876
			private IObserver<T> unsubscribeTarget;
		}
	}
}
